
package com.ys.chatserver.http.file.d;

import com.alibaba.fastjson.JSONObject;
import com.eva.epc.common.util.CommonUtils;
import com.eva.framework.utils.LoggerFactory;
import com.ys.chatserver.http.file.u.BigFileUploder;
import com.ys.chatserver.http.logic.filter.JwtHelper;
import com.ys.chatserver.http.logic.filter.JwtParam;
import com.ys.chatserver.tool.EmptyUtils;
import com.ys.chatserver.tool.MD5Util;
import org.slf4j.Logger;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.HashMap;
import java.util.Map;

/**
 * 通用大文件下载类（支持从指定断点处下载）。
 *
 * @author JackJiang
 * @since 4.3
 */
//FIXME: 在更苛刻的安全性要求下，可考虑要求客户端把token字段带上来，以便进行更强的合法身份检查
public class BigFileDownloader extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
    public static final String CONTENT_TYPE_DEFAULT = "application/octet-stream;charset=UTF-8";
    private static final Logger logger = LoggerFactory.getLog();

    /**
     * Servlet规范中本protected方法由public void service(,,)方法调用, 而public void service(,,)
     * 则由Web容器调用(所以它是public公共方法),因此一般情况下尽量避免重写public void service(,,)方法
     * ,而重写父类的本protedted方法则比较合理.
     */
    protected void service(HttpServletRequest req, HttpServletResponse res) throws IOException {
        // 此行代码解决网页端上传的中文文件名后，服务端读取到的是乱码的问题
        req.setCharacterEncoding("UTF-8");

        LoggerFactory.getLog().debug("[HTTP][大文件下载]下载大文件：你传过来的参数"
                + "file_md5=" + req.getParameter("file_md5")
                + ",skipLength=" + req.getParameter("skip_length")
                + ",file_name=" + req.getParameter("file_name")
                + ",[---- " + req.getContextPath() + "-" + req.getRequestURL() + "]");

        String file_name = req.getParameter("file_name");
        String file_md5 = req.getParameter("file_md5");
        // 合法性检查（tomcat9中，URLEncoder.encode(null) 将会发生空指针异常）
        if (file_name == null)
            file_name = "";

        // 要跳过的字节数（为了支持断点下载嘛）
        long skipLength = 0;
        String skipLength_s = req.getParameter("skip_length");

        BufferedOutputStream output = null;
        BufferedInputStream input = null;

        // 本次下载已成功读取并传输给客户端的字节数（本字段仅用于log中记录）
        long totoalRead = 0;
        try {
            try {
                skipLength = Long.parseLong(skipLength_s);
                // 修正不合法的参数值
                if (skipLength < 0) {
                    LoggerFactory.getLog().debug("[HTTP][大文件下载] 参数skipLength_s"
                            + "解析完成，结果不合法：" + skipLength + ", 将被默认修正为0!");
                    skipLength = 0;
                }
            } catch (Exception e) {
                LoggerFactory.getLog().warn("[HTTP][大文件下载] 解析参数skipLength_s出错，skipLength_s=" + skipLength_s);
            }
            if (CommonUtils.isStringEmpty(file_md5, true))
                throw new Exception("无效的参数，file_md5不可为空！");
            // 确保使用小写字母，与其它各种保持一致
            file_md5 = file_md5.toLowerCase();

//			String filePath = BigFileUploder.getFileSavePath(file_md5);
            String filePath = BigFileUploder.getCommonBigFileSavePath(file_md5);
            File f = new File(filePath);
            if (!f.exists())
                throw new Exception("要下载的文件" + filePath + "不存在于服务端磁盘上！");

            if (f.length() <= skipLength)
                throw new Exception("服务端文件小于预期，f.len=" + f.length()
                        + ", skipLength=" + skipLength + "！");

            LoggerFactory.getLog().debug("[HTTP][大文件下载] 马上要从" + filePath
                    + "下载文件了，skipLength=" + skipLength + ",f.length()=" + f.length());
            res.setContentLength((int) (f.length() - skipLength));
            res.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + java.net.URLEncoder.encode(file_name, "UTF-8"));
            int bufferSize = 1024;
            output = new BufferedOutputStream(res.getOutputStream());
            input = new BufferedInputStream(new FileInputStream(f));

            // 跳过用户之前已经下载过的字节（实现断点续传）
            if (skipLength > 0)
                input.skip(skipLength);
            int n = 0;
            byte[] buffer = new byte[bufferSize];
            while ((n = input.read(buffer)) != -1) {
                output.write(buffer, 0, n);
                totoalRead += n;
            }
            res.flushBuffer();
            LoggerFactory.getLog().debug("[HTTP][大文件下载]【下载成功完成】从" + filePath
                    + "下载完成了(本次客户端共下载" + totoalRead + "字节)！(skipLength=" + skipLength + ",f.length()=" + f.length() + ")");
        } catch (Exception e) {
            String l = "[HTTP][大文件下载]【下载提前结束】任务没有继续(本次客户端共下载" + totoalRead + "字节)，原因是："
                    + e.getMessage() + "(file_name=" + file_name + ",file_md5=" + file_md5 + ",skipLength=" + skipLength + ")";
            LoggerFactory.getLog().debug(l, e);
            res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            res.getWriter().write(l);
        } finally {
            try {
                if (input != null)
                    input.close();
                if (output != null)
                    output.close();
            } catch (Exception e2) {
                logger.error(e2.getMessage(), e2);
            }
        }
    }

}
